using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading;
using Ns.Properties;

namespace Ns.Collections.Generic
{
    /// <summary>
    /// Represents a <see cref="T:System.Collections.Generic.Dictionary`2"/> whose pairs are removed after a specified <see cref="Timeout"/>.
    /// A <see cref="System.Threading.Timer"/> removes at every <see cref="Period"/> pairs timed out.
    /// Depending on <see cref="IsRenewed"/> property, the lifespan of the pairs is automatically renewed or not each time they are accessed.
    /// </summary>
    /// <typeparam name="TKey">The type of the keys in the dictionary.</typeparam>
    /// <typeparam name="TValue">The type of the values in the dictionary.</typeparam>
    /// <remarks>
    /// <para>Type <typeparamref name="TValue"/> implementing the <see cref="IDisposable"/> interface are also disposed when removed by default.</para>
    /// <para>A <see cref="TimedDictionary&lt;TKey, TValue&gt;"/> could not behave as expected if specified <see cref="Timeout"/> and/or <see cref="Period"/> are below one second.</para>
    /// </remarks>
    [Serializable, DebuggerDisplay("Count = {RawCount}"), ComVisible(false)]
    //[DebuggerNonUserCode]
    public class TimedDictionary<TKey, TValue> :
        IDictionary<TKey, TValue>,
        IDictionary,
        IDisposable
    {
        #region TimedValue
        /// <summary>
        /// Encapsulates an instance of a <typeparamref name="TValue"/> with a timestamp.
        /// </summary>
        /// <typeparam name="T">The <typeparamref name="TValue"/> type.</typeparam>
        /// <remarks>The timestamp is just informative. The overriden methods <see cref="M:Equals"/> and <see cref="M:GetHashCode"/> ignore it.</remarks>
        private class TimedValue<T> :
            IEquatable<T>,
            IEquatable<TimedValue<T>>,
            IDisposable
            where T : TValue
        {
            #region Fields

            private DateTimeOffset timestamp;
            private readonly T value;
            private bool isDisposed;

            #endregion

            #region Constructors

            public TimedValue(T value)
                : this(value, DateTimeOffset.Now)
            {
            }

            public TimedValue(T value, DateTimeOffset timestamp)
            {
                this.value = value;
                this.timestamp = timestamp;
                this.isDisposed = false;
            }

            #endregion

            #region Operators

            public static implicit operator TimedValue<T>(T value)
            {
                return new TimedValue<T>(value);
            }

            public static implicit operator T(TimedValue<T> value)
            {
                return value.value;
            }

            public static implicit operator DateTimeOffset(TimedValue<T> value)
            {
                return value.timestamp;
            }

            #endregion

            #region Methods

            public void Renew()
            {
                Reset(DateTimeOffset.Now);
            }

            public void Reset(DateTimeOffset timestamp)
            {
                this.timestamp = timestamp;
            }

            public bool TimedOut(DateTimeOffset timeout)
            {
                return timeout >= timestamp;
            }

            public override string ToString()
            {
                return string.Format("Timestamp = {0}, Value = {1}", timestamp, value);
            }

            public override int GetHashCode()
            {
                int hash = 0x4043ed47;
                hash = (hash * -1521134295) + timestamp.GetHashCode();
                hash = (hash * -1521134295) + (object.Equals(value, default(T)) ? 0 : value.GetHashCode());
                return hash;
            }

            public override bool Equals(object obj)
            {
                return (obj is TimedValue<T>)
                    ? Equals((TimedValue<T>)obj)
                    : false;
            }

            public bool Equals(TimedValue<T> other)
            {
                return object.ReferenceEquals(this, other) ||
                    (other != null
                    && object.Equals(value, other.value));
            }

            public bool Equals(T other)
            {
                return object.Equals(value, other);
            }

            #region IDisposable Members

            public void Dispose()
            {
                Dispose(true);
                GC.SuppressFinalize(this);
            }

            private void Dispose(bool disposing)
            {
                if (!isDisposed)
                {
                    if (disposing)
                    {
                        if (value != null &&
                            value is IDisposable)
                        {
                            ((IDisposable)value).Dispose();
                        }
                    }
                    isDisposed = true;
                }
            }

            #endregion

            #endregion
        }

        #endregion

        #region Fields
        private Guid instanceId = Guid.NewGuid();
        private readonly object syncRoot = new object();
        private readonly Dictionary<TKey, TimedValue<TValue>> innerDictionary;
        private Timer ticker;
        private bool isDisposed;

        #endregion

        #region Properties

        private readonly bool _isDisposingValues;

        /// <summary>
        /// Indicates if values are also disposed once their pair timeout is reached. It is applicable only if the <typeparamref name="TValue"/> type is implementing the <see cref="IDisposable"/> interface.
        /// </summary>
        public bool IsDisposingValues { get { return _isDisposingValues; } }

        private readonly bool _isRenewed;

        /// <summary>
        /// Indicates if <see cref="TimedDictionary&lt;TKey, TValue&gt;"/> pairs lifespan is automatically renewed or not each time pairs are accessed.
        /// </summary>
        public bool IsRenewed { get { return _isRenewed; } }

        private readonly TimeSpan _timeout;

        /// <summary>
        /// Gets the lifespan of <see cref="TimedDictionary&lt;TKey, TValue&gt;"/> pairs. 
        /// </summary>
        public TimeSpan Timeout { get { return _timeout; } }

        private readonly TimeSpan _period;

        /// <summary>
        /// Gets the interval of time between each check on pairs lifespan.
        /// </summary>
        public TimeSpan Period { get { return _period; } }

        #endregion

        #region Constructors

        /// <summary>
        /// Initializes a new instance of the <see cref="TimedDictionary&lt;TKey, TValue&gt;"/> class 
        /// that is empty, has the default initial capacity, 
        /// uses the default equality comparer for the key type 
        /// and whose the lifespan of the pairs is infinit. 
        /// </summary>
        public TimedDictionary()
            : this(TimeSpan.Zero)
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="TimedDictionary&lt;TKey, TValue&gt;"/> class 
        /// that is empty, has the default initial capacity, 
        /// uses the default equality comparer for the key type,
        /// whose lifespan of the pairs corresponds to the specified <paramref name="timeout"/> 
        /// and is automatically renewed each time they are accessed.
        /// </summary>
        /// <param name="timeout">The time interval after which a pair is removed from the <see cref="TimedDictionary&lt;TKey, TValue&gt;"/>.</param>
        /// <remarks>In this constructor, the <see cref="Period"/> equals the specified <paramref name="timeout"/>.</remarks>
        /// <exception cref="T:System.ArgumentOutOfRangeException"><paramref name="timeout"/> is less than zero.</exception>
        public TimedDictionary(TimeSpan timeout)
            : this(timeout, true)
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="TimedDictionary&lt;TKey, TValue&gt;"/> class 
        /// that is empty, has the default initial capacity, 
        /// uses the default equality comparer for the key type 
        /// and whose lifespan of the pairs corresponds to the specified <paramref name="timeout"/>. 
        /// </summary>
        /// <param name="isRenewed">Indicates that pairs lifespan is automatically renewed or not each time they are accessed.</param>
        /// <param name="timeout">The time interval after which a pair is removed from the <see cref="TimedDictionary&lt;TKey, TValue&gt;"/>.</param>
        /// <remarks>In this constructor, the <see cref="Period"/> equals the specified <paramref name="timeout"/>.</remarks>
        /// <exception cref="T:System.ArgumentOutOfRangeException"><paramref name="timeout"/> is less than zero.</exception>
        public TimedDictionary(TimeSpan timeout, bool isRenewed)
            : this(timeout, timeout, isRenewed)
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="TimedDictionary&lt;TKey, TValue&gt;"/> class 
        /// that is empty, has the default initial capacity, 
        /// uses the default equality comparer for the key type, 
        /// whose lifespan of the pairs corresponds to the specified <paramref name="timeout"/> 
        /// and is automatically renewed each time they are accessed.
        /// </summary>
        /// <param name="timeout">The time interval after which a pair is removed from the <see cref="TimedDictionary&lt;TKey, TValue&gt;"/>.</param>
        /// <param name="period">The time interval between checks on pairs timeout.</param>
        /// <exception cref="T:System.ArgumentOutOfRangeException"><paramref name="timeout"/> or <paramref name="period"/> is less than zero.</exception>
        public TimedDictionary(TimeSpan timeout, TimeSpan period)
            : this(timeout, period, true)
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="TimedDictionary&lt;TKey, TValue&gt;"/> class 
        /// that is empty, has the default initial capacity, 
        /// uses the default equality comparer for the key type 
        /// and whose lifespan of the pairs corresponds to the specified <paramref name="timeout"/>.
        /// </summary>        
        /// <param name="timeout">The time interval after which a pair is removed from the <see cref="TimedDictionary&lt;TKey, TValue&gt;"/>.</param>
        /// <param name="period">The time interval between checks on pairs timeout.</param>
        /// <param name="isRenewed">Indicates that pairs lifespan is automatically renewed or not each time they are accessed.</param>
        /// <exception cref="T:System.ArgumentOutOfRangeException"><paramref name="timeout"/> or <paramref name="period"/> is less than zero.</exception>
        public TimedDictionary(TimeSpan timeout, TimeSpan period, bool isRenewed)
            : this(timeout, period, isRenewed, typeof(IDisposable).IsAssignableFrom(typeof(TValue)))
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="TimedDictionary&lt;TKey, TValue&gt;"/> class 
        /// that contains elements copied from the specified <paramref name="dictionary"/>        
        /// uses the default equality comparer for the key type 
        /// and whose lifespan of the pairs corresponds to the specified <paramref name="timeout"/>.
        /// </summary>
        /// <param name="timeout">The time interval after which a pair is removed from the <see cref="TimedDictionary&lt;TKey, TValue&gt;"/>.</param>
        /// <param name="period">The time interval between checks on pairs timeout.</param>
        /// <param name="isRenewed">Indicates that pairs lifespan is automatically renewed or not each time they are accessed.</param>
        /// <param name="dictionary">The <see cref="T:System.Collections.Generic.IDictionary`2"/> whose elements are copied to the new <see cref="TimedDictionary&lt;TKey, TValue&gt;"/>.</param>
        /// <exception cref="T:System.ArgumentOutOfRangeException"><paramref name="timeout"/> or <paramref name="period"/> is less than zero.</exception>
        /// <exception cref="T:System.ArgumentNullException"><paramref name="dictionary"/> is <b>null</b>.</exception>                        
        public TimedDictionary(TimeSpan timeout, TimeSpan period, bool isRenewed, IDictionary<TKey, TValue> dictionary)
            : this(timeout, period, isRenewed, typeof(IDisposable).IsAssignableFrom(typeof(TValue)))
        {
            foreach (var pair in dictionary)
            {
                Add(pair.Key, pair.Value);
            }
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="TimedDictionary&lt;TKey, TValue&gt;"/> class 
        /// that is empty, has the default initial capacity, 
        /// uses the default equality comparer for the key type 
        /// and whose lifespan of the pairs corresponds to the specified <paramref name="timeout"/>.
        /// </summary>        
        /// <param name="timeout">The time interval after which a pair is removed from the <see cref="TimedDictionary&lt;TKey, TValue&gt;"/>.</param>
        /// <param name="period">The time interval between checks on pairs timeout.</param>
        /// <param name="isRenewed">Indicates that pairs lifespan is automatically renewed or not each time they are accessed.</param>
        /// <param name="isDisposingValues">Indicates that pair values are disposed or not once pairs time out.</param>
        /// <exception cref="T:System.ArgumentOutOfRangeException"><paramref name="timeout"/> or <paramref name="period"/> is less than zero.</exception>
        /// <exception cref="T:System.ArgumentException"><paramref name="isDisposingValues"/> is set to <b>true</b> without finding implementation of the <see cref="IDisposable"/> interface for type <typeparamref name="TValue"/>.</exception>        
        public TimedDictionary(TimeSpan timeout, TimeSpan period, bool isRenewed, bool isDisposingValues) :
            this(timeout, period, isRenewed, isDisposingValues, new Dictionary<TKey, TimedValue<TValue>>())
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="TimedDictionary&lt;TKey, TValue&gt;"/> class 
        /// that is empty, has the specified initial <paramref name="capacity"/>, 
        /// uses the specified <paramref name="comparer"/> for the key type
        /// and whose lifespan of the pairs corresponds to the specified <paramref name="timeout"/>.
        /// </summary>
        /// <param name="timeout">The time interval after which a pair is removed from the <see cref="TimedDictionary&lt;TKey, TValue&gt;"/>.</param>
        /// <param name="period">The time interval between checks on pairs timeout.</param>
        /// <param name="isRenewed">Indicates that pairs lifespan is automatically renewed or not each time they are accessed.</param>
        /// <param name="isDisposingValues">Indicates that pair values are disposed or not once pairs time out.</param>
        /// <param name="capacity">The initial number of elements that the <see cref="TimedDictionary&lt;TKey, TValue&gt;"/> can contain.</param>
        /// <param name="comparer">The <see cref="T:System.Collections.Generic.IEqualityComparer`1"/> implementation to use when comparing keys, or a <b>null</b> reference to use the default <see cref="T:System.Collections.Generic.EqualityComparer`1"/> for the type of the key.</param>
        /// <exception cref="T:System.ArgumentOutOfRangeException"><paramref name="timeout"/> or <paramref name="period"/> or <paramref name="capacity"/> is less than zero.</exception>        
        /// <exception cref="T:System.ArgumentException"><paramref name="isDisposingValues"/> is set to <b>true</b> without finding implementation of the <see cref="IDisposable"/> interface for type <typeparamref name="TValue"/>.</exception>
        public TimedDictionary(TimeSpan timeout, TimeSpan period, bool isRenewed, bool isDisposingValues, int capacity, IEqualityComparer<TKey> comparer) :
            this(timeout, period, isRenewed, isDisposingValues, new Dictionary<TKey, TimedValue<TValue>>(capacity, comparer))
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="TimedDictionary&lt;TKey, TValue&gt;"/> class 
        /// that contains elements copied from the specified <paramref name="dictionary"/>        
        /// uses the default equality comparer for the key type 
        /// and whose lifespan of the pairs corresponds to the specified <paramref name="timeout"/>.
        /// </summary>
        /// <param name="timeout">The time interval after which a pair is removed from the <see cref="TimedDictionary&lt;TKey, TValue&gt;"/>.</param>
        /// <param name="period">The time interval between checks on pairs timeout.</param>
        /// <param name="isRenewed">Indicates that pairs lifespan is automatically renewed or not each time they are accessed.</param>
        /// <param name="isDisposingValues">Indicates that pair values are disposed or not once pairs time out.</param>
        /// <param name="dictionary">The <see cref="T:System.Collections.Generic.IDictionary`2"/> whose elements are copied to the new <see cref="TimedDictionary&lt;TKey, TValue&gt;"/>.</param>
        /// <exception cref="T:System.ArgumentOutOfRangeException"><paramref name="timeout"/> or <paramref name="period"/> is less than zero.</exception>
        /// <exception cref="T:System.ArgumentException"><paramref name="isDisposingValues"/> is set to <b>true</b> without finding implementation of the <see cref="IDisposable"/> interface for type <typeparamref name="TValue"/>.</exception>
        /// <exception cref="T:System.ArgumentNullException"><paramref name="dictionary"/> is <b>null</b>.</exception>                
        private TimedDictionary(TimeSpan timeout, TimeSpan period, bool isRenewed, bool isDisposingValues, Dictionary<TKey, TimedValue<TValue>> dictionary)
        {
            if (timeout < TimeSpan.Zero)
                throw new ArgumentOutOfRangeException(string.Format(Resources.ArgumentOutOfRange_MustBePositive, "timeout"), "timeout");
            if (period < TimeSpan.Zero)
                throw new ArgumentOutOfRangeException(string.Format(Resources.ArgumentOutOfRange_MustBePositive, "period"), "period");
            if (isDisposingValues && !typeof(IDisposable).IsAssignableFrom(typeof(TValue)))
                throw new ArgumentException(Resources.Argument_IDisposableNotFound, "isDisposingValues");
            if (dictionary == null)
                throw new ArgumentNullException("dictionary");

            _timeout = timeout;
            _period = period;
            _isRenewed = isRenewed;
            _isDisposingValues = isDisposingValues;
            innerDictionary = dictionary;
            
            Debug.WriteLine("A new dictionary ({0}) has been instantiated with the following parameters: Timeout = {1} Period = {2} IsRenewed = {3} IsDisposingValues = {4}"
                , instanceId
                , timeout
                , period
                , isRenewed
                , isDisposingValues);

            CheckStart();
        }

        #endregion

        #region IDictionary<TKey,TValue> Members

        /// <summary>
        /// Determines whether the <see cref="TimedDictionary&lt;TKey, TValue&gt;"/> contains the specified key.
        /// </summary>
        /// <param name="key">The key to locate in the <see cref="TimedDictionary&lt;TKey, TValue&gt;"/>.</param>
        /// <returns><b>true</b> if the <see cref="T:System.Collections.Generic.Dictionary`2"/> contains an element with the specified key; otherwise, <b>false.</b></returns>
        /// <exception cref="T:System.ArgumentNullException"><paramref name="key"/> is <b>null</b>.</exception>
        /// <remarks>Calling this method renews pair lifespan if <see cref="IsRenewed"/> equals <b>true</b>.</remarks>
        public bool ContainsKey(TKey key)
        {
            if (!TimeoutEnabled)
                return innerDictionary.ContainsKey(key);
            else
            {
                lock (syncRoot)
                {
                    TimedValue<TValue> timedValue;
                    if (innerDictionary.TryGetValue(key, out timedValue))
                        return IsEntryAlive(key, timedValue);
                    return false;
                }
            }
        }

        /// <summary>
        /// Adds the specified key and value to the <see cref="TimedDictionary&lt;TKey, TValue&gt;"/>. 
        /// </summary>
        /// <param name="key">The key of the element to add.</param>
        /// <param name="value">The value of the element to add. The value can be <b>null</b> for reference types.</param>
        /// <exception cref="T:System.ArgumentNullException"><paramref name="key"/> is <b>null</b>.</exception>
        /// <exception cref="T:System.ArgumentException">An element with the same key already exists in the <see cref="TimedDictionary&lt;TKey, TValue&gt;"/>.</exception>
        public void Add(TKey key, TValue value)
        {
            lock (syncRoot)
            {
                innerDictionary.Add(key, value);
                Debug.WriteLine("The following key/value pair has been added to the dictionary ({0}): {1}/{2}"
                    , instanceId
                    , key
                    , value);
                CheckStart();
            }
        }

        /// <summary>
        /// Removes the value with the specified key from the <see cref="TimedDictionary&lt;TKey, TValue&gt;"/> and disposes it if applicable.
        /// </summary>
        /// <param name="key">The key of the element to remove.</param>
        /// <returns><b>true</b> if the element is successfully found and removed; otherwise, <b>false</b>. This method returns false if <paramref name="key"/> is not found in the <see cref="TimedDictionary&lt;TKey, TValue&gt;"/>.</returns>
        /// <exception cref="T:System.ArgumentNullException"><paramref name="key"/> is <b>null</b>.</exception>
        public bool Remove(TKey key)
        {
            return Remove(key, IsDisposingValues);
        }

        /// <summary>
        /// Removes the value with the specified key from the <see cref="TimedDictionary&lt;TKey, TValue&gt;"/> and disposes it optionally.
        /// </summary>
        /// <param name="key">The key of the element to remove.</param>
        /// <param name="dispose">if set to <c>true</c> and the element associated with the specified <paramref name="key"/> implements <see cref="IDisposable"/> disposes it; otherwise not.</param>
        /// <returns><b>true</b> if the element is successfully found and removed; otherwise, <b>false</b>. This method returns false if <paramref name="key"/> is not found in the <see cref="TimedDictionary&lt;TKey, TValue&gt;"/>.</returns>
        /// <exception cref="T:System.ArgumentNullException"><paramref name="key"/> is <b>null</b>.</exception>
        public bool Remove(TKey key, bool dispose)
        {
            lock (syncRoot)
            {
                if (dispose
                    && typeof(IDisposable).IsAssignableFrom(typeof(TValue)))
                {
                    TimedValue<TValue> value;
                    if (innerDictionary.TryGetValue(key, out value))
                        value.Dispose();
                }

                bool result = innerDictionary.Remove(key);
                Debug.WriteLine("The following key has been removed from the dictionary ({0}): {1}"
                    , instanceId
                    , key);
                if (result)
                {
                    CheckStop();
                }
                return result;
            }
        }

        /// <summary>
        /// Gets the value associated with the specified key. 
        /// A return value indicates whether the operation succeeded.
        /// </summary>
        /// <param name="key">The key of the value to get.</param>
        /// <param name="value">When this method returns, contains the value associated with the specified key, if the key is found and not timeout; otherwise, the default value for the type of the <paramref name="value"/> parameter. This parameter is passed uninitialized.</param>
        /// <returns><b>true</b> if the <see cref="TimedDictionary&lt;TKey, TValue&gt;"/> contains an element with the specified key; otherwise, <b>false</b>.</returns>
        /// <exception cref="T:System.ArgumentNullException"><paramref name="key"/> is <b>null</b>.</exception>
        /// <remarks>Calling this method renews pair lifespan if <see cref="IsRenewed"/> equals <b>true</b>.</remarks>
        public bool TryGetValue(TKey key, out TValue value)
        {
            TimedValue<TValue> timedValue;

            if (!TimeoutEnabled)
            {
                if (innerDictionary.TryGetValue(key, out timedValue))
                {
                    value = timedValue;
                    return true;
                }
            }
            else
            {
                lock (syncRoot)
                {
                    if (innerDictionary.TryGetValue(key, out timedValue))
                    {
                        if (IsEntryAlive(key, timedValue))
                        {
                            value = timedValue;
                            return true;
                        }
                    }
                }
            }

            value = default(TValue);
            return false;
        }

        /// <summary>
        /// Gets the value associated with the specified key using the fetching <see cref="Delegate"/> if not found.
        /// </summary>
        /// <param name="key">The key of the value to get.</param>
        /// <param name="fetchOfMissing">The <see cref="Delegate"/> fetching the value if missing.</param>
        /// <returns>The value associated with the specified key.</returns>
        /// <remarks>If the specified key is not found, this methods fetches the value using the fetching <see cref="Delegate"/>, adds the corresponding pair to the <see cref="TimedDictionary&lt;TKey, TValue&gt;"/> and returns the fetched value.</remarks>
        /// <example>
        /// In the example below, the fetching delegate should normally be replaced by a method call.
        /// <code>
        ///     var dictionary = new TimedDictionary&lt;int, string&gt;();
        ///     dictionary.GetValue(0, () => 
        ///     {
        ///         return string.Empty;
        ///     });
        /// </code>
        /// </example>
        public TValue GetValue(TKey key, Func<TValue> fetchOfMissing)
        {
            lock (syncRoot)
            {
                var value = default(TValue);
                if (!TryGetValue(key, out value))
                {
                    value = fetchOfMissing();
                    Add(key, value);
                }
                return value;
            }
        }

        /// <summary>
        /// Extracts the value associated with the specified key from the <see cref="TimedDictionary&lt;TKey, TValue&gt;"/>. 
        /// </summary>
        /// <param name="key">The key of the value to extract.</param>
        /// <param name="value">When this method returns, contains the value associated with the specified key, if the key is found and not timeout; otherwise, the default value for the type of the <paramref name="value"/> parameter. This parameter is passed uninitialized.</param>        
        /// <returns><b>true</b> if the <see cref="TimedDictionary&lt;TKey, TValue&gt;"/> contains an element with the specified key; otherwise, <b>false</b>.</returns>
        /// <exception cref="T:System.ArgumentNullException"><paramref name="key"/> is <b>null</b>.</exception>
        public bool TryExtractValue(TKey key, out TValue value)
        {
            TimedValue<TValue> timedValue;

            if (!TimeoutEnabled)
            {
                if (innerDictionary.TryGetValue(key, out timedValue))
                {
                    innerDictionary.Remove(key);
                    Debug.WriteLine("The following key has been removed from the dictionary ({0}): {1}"
                        , instanceId
                        , key);
                    value = timedValue;
                    return true;
                }
            }
            else
            {
                lock (syncRoot)
                {
                    if (innerDictionary.TryGetValue(key, out timedValue))
                    {
                        if (IsEntryAlive(key, timedValue))
                        {
                            Remove(key, false);
                            value = timedValue;
                            return true;
                        }
                    }
                }
            }

            value = default(TValue);
            return false;
        }

        /// <summary>
        /// Gets or sets the value associated with the specified key. 
        /// </summary>
        /// <param name="key">The key of the value to get or set.</param>
        /// <returns>The value associated with the specified key. If the specified key is not found or is timeout, a get operation throws a <see cref="KeyNotFoundException"/>, and a set operation creates a new element with the specified key.</returns>
        /// <exception cref="T:System.ArgumentNullException"><paramref name="key"/> is <b>null</b>.</exception>
        /// <exception cref="KeyNotFoundException">The property is retrieved and <paramref name="key"/> does not exist in the collection.</exception>
        /// <remarks>Getting this property renews pair lifespan if <see cref="IsRenewed"/> equals <b>true</b>.</remarks>
        public TValue this[TKey key]
        {
            get
            {
                if (!TimeoutEnabled)
                    return innerDictionary[key];
                else
                {
                    lock (syncRoot)
                    {
                        TimedValue<TValue> result = innerDictionary[key];
                        if (IsEntryAlive(key, result))
                            return result;
                        else
                            throw new KeyNotFoundException();
                    }
                }
            }
            set
            {
                lock (syncRoot)
                {
                    innerDictionary[key] = value;
                    Debug.WriteLine("The following key/value pair has been added to the dictionary ({0}): {1}/{2}"
                        , instanceId
                        , key
                        , value);
                    CheckStart();
                }
            }
        }

        /// <summary>
        /// Gets an <see cref="T:System.Collections.Generic.ICollection`1"/> containing the keys in the <see cref="T:System.Collections.Generic.IDictionary`2"/>.
        /// </summary>
        /// <returns>
        /// An <see cref="T:System.Collections.Generic.ICollection`1"/> of type <typeparamref name="TKey"/> containing the keys in the <see cref="T:System.Collections.Generic.IDictionary`2"/>.
        /// </returns>
        ICollection<TKey> IDictionary<TKey,TValue>.Keys
        {
            get
            {
                lock (syncRoot)
                {
                    return innerDictionary.Keys;
                }
            }
        }

        /// <summary>
        /// Gets an <see cref="T:System.Collections.Generic.ICollection`1"/> containing the values in the <see cref="T:System.Collections.Generic.IDictionary`2"/>.
        /// </summary>
        /// <returns>
        /// An <see cref="T:System.Collections.Generic.ICollection`1"/> of type <typeparamref name="TValue"/> containing the values in the <see cref="T:System.Collections.Generic.IDictionary`2"/>.
        /// </returns>
        ICollection<TValue> IDictionary<TKey,TValue>.Values
        {
            get
            {
                lock (syncRoot)
                {
                    return (from value in innerDictionary.Values
                            select (TValue)value).ToList();
                }
            }
        }

        #endregion

        #region ICollection<KeyValuePair<TKey,TValue>> Members

        /// <summary>
        /// Adds the specified pair.
        /// </summary>
        /// <param name="pair">The pair.</param>
        void ICollection<KeyValuePair<TKey, TValue>>.Add(KeyValuePair<TKey, TValue> pair)
        {
            Add(pair.Key, pair.Value);
        }

        /// <summary>
        /// Removes all keys and values from the <see cref="TimedDictionary&lt;TKey, TValue&gt;"/>. 
        /// </summary>
        public void Clear()
        {
            lock (syncRoot)
            {
                if (IsDisposingValues)
                {
                    foreach (var value in innerDictionary.Values)
                        value.Dispose();
                }

                innerDictionary.Clear();
                Debug.WriteLine("The dictionary ({0}) has been cleared."
                    , instanceId);
                CheckStop();
            }
        }

        /// <summary>
        /// Determines whether the <see cref="TimedDictionary&lt;TKey, TValue&gt;"/> contains the specified pair.
        /// </summary>
        /// <param name="pair">The pair to locate in the <see cref="TimedDictionary&lt;TKey, TValue&gt;"/>.</param>        
        /// <returns><b>true</b> if the <see cref="TimedDictionary&lt;TKey, TValue&gt;"/> contains the specified pair; otherwise, <b>false.</b></returns>
        /// <exception cref="T:System.ArgumentNullException">The <see cref="P:KeyValuePair`2.Key"/> is <b>null</b>.</exception>
        /// <remarks>Calling this method renews pair lifespan if <see cref="IsRenewed"/> equals <b>true</b>.</remarks>
        bool ICollection<KeyValuePair<TKey, TValue>>.Contains(KeyValuePair<TKey, TValue> pair)
        {
            return ContainsKey(pair.Key);
        }

        /// <summary>
        /// Copies the elements of the <see cref="T:System.Collections.Generic.ICollection`1"/> to an <see cref="T:System.Array"/>, starting at a particular <see cref="T:System.Array"/> index.
        /// </summary>
        /// <param name="array">The one-dimensional <see cref="T:System.Array"/> that is the destination of the elements copied from <see cref="T:System.Collections.Generic.ICollection`1"/>. The <see cref="T:System.Array"/> must have zero-based indexing.</param>
        /// <param name="arrayIndex">The zero-based index in <paramref name="array"/> at which copying begins.</param>
        /// <exception cref="T:System.ArgumentNullException">
        /// 	<paramref name="array"/> is null.
        /// </exception>
        /// <exception cref="T:System.ArgumentOutOfRangeException">
        /// 	<paramref name="arrayIndex"/> is less than 0.
        /// </exception>
        /// <exception cref="T:System.ArgumentException">
        /// <paramref name="array"/> is multidimensional.
        /// -or-
        /// <paramref name="arrayIndex"/> is equal to or greater than the length of <paramref name="array"/>.
        /// -or-
        /// The number of elements in the source <see cref="T:System.Collections.Generic.ICollection`1"/> is greater than the available space from <paramref name="arrayIndex"/> to the end of the destination <paramref name="array"/>.
        /// </exception>
        void ICollection<KeyValuePair<TKey, TValue>>.CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
        {
            if (array == null)
            {
                throw new ArgumentNullException("array");
            }

            lock (syncRoot)
            {
                var innerArray = new KeyValuePair<TKey, TimedValue<TValue>>[array.Length];
                ((ICollection<KeyValuePair<TKey, TimedValue<TValue>>>)innerDictionary).CopyTo(innerArray, arrayIndex);
                for (var i = 0; i < innerArray.Length; i++)
                {
                    array[i] = new KeyValuePair<TKey, TValue>(innerArray[i].Key, innerArray[i].Value);
                }
            }
        }

        /// <summary>
        /// Removes the first occurrence of a specific object from the <see cref="T:System.Collections.Generic.ICollection`1"/>.
        /// </summary>
        /// <param name="item">The object to remove from the <see cref="T:System.Collections.Generic.ICollection`1"/>.</param>
        /// <returns>
        /// true if <paramref name="item"/> was successfully removed from the <see cref="T:System.Collections.Generic.ICollection`1"/>; otherwise, false. This method also returns false if <paramref name="item"/> is not found in the original <see cref="T:System.Collections.Generic.ICollection`1"/>.
        /// </returns>
        /// <exception cref="T:System.NotSupportedException">
        /// The <see cref="T:System.Collections.Generic.ICollection`1"/> is read-only.
        /// </exception>
        bool ICollection<KeyValuePair<TKey, TValue>>.Remove(KeyValuePair<TKey, TValue> item)
        {
            return Remove(item.Key);
        }

        /// <summary>
        /// Gets the number of key/value pairs contained in the <see cref="TimedDictionary&lt;TKey, TValue&gt;"/> with taking into account the <see cref="Timeout"/>.
        /// </summary>
        public int Count
        {
            get
            {
                if (!TimeoutEnabled)
                {
                    return innerDictionary.Count;
                }
                else
                {
                    lock (syncRoot)
                    {
                        RemoveObsoletePairs();
                        return innerDictionary.Count;
                    }
                }
            }
        }

        bool ICollection<KeyValuePair<TKey, TValue>>.IsReadOnly
        {
            get { return false; }
        }

        #endregion

        #region IEnumerable<KeyValuePair<TKey,TValue>> Members

        IEnumerator<KeyValuePair<TKey, TValue>> IEnumerable<KeyValuePair<TKey, TValue>>.GetEnumerator()
        {            
            lock (syncRoot)
            {
                var dictionary = new Dictionary<TKey, TValue>(innerDictionary.Count);
                foreach (var pair in innerDictionary)
                {
                    dictionary.Add(pair.Key, pair.Value);
                }
                return dictionary.GetEnumerator();
            }
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return ((IEnumerable<KeyValuePair<TKey, TValue>>)this).GetEnumerator();
        }

        #endregion

        #region Timed Dictionary method

        /// <summary>
        /// Provides a shallow copy of current pairs timestamp.
        /// </summary>
        public Dictionary<TKey, DateTimeOffset> Timestamps
        {
            get
            {
                var dictionary = new Dictionary<TKey, DateTimeOffset>(innerDictionary.Count);

                lock (syncRoot)
                {
                    foreach (var pair in innerDictionary)
                    {
                        dictionary.Add(pair.Key, pair.Value);
                    }
                }

                return dictionary;
            }
        }

        /// <summary>
        /// Reset pairs <see cref="Timeout"/> to <see cref="DateTimeOffset.Now"/>.
        /// </summary>
        public void Reset()
        {
            Reset(DateTimeOffset.Now);
        }

        /// <summary>
        /// Reset pairs <see cref="Timeout"/>.
        /// </summary>
        /// <param name="timestamp">The new <paramref name="timestamp"/> applied to all pairs.</param>
        public void Reset(DateTimeOffset timestamp)
        {
            lock (syncRoot)
            {
                foreach (var pair in innerDictionary)
                {
                    pair.Value.Reset(timestamp);
                }
            }
        }

        /// <summary>
        /// Determines whether the <see cref="TimedDictionary&lt;TKey, TValue&gt;"/> contains a specific value. 
        /// </summary>
        /// <param name="value">The value to locate in the <see cref="TimedDictionary&lt;TKey, TValue&gt;"/>. The value can be <b>null</b> for reference types.</param>
        /// <returns><b>true</b> if the <see cref="TimedDictionary&lt;TKey, TValue&gt;"/> contains an element with the specified value; otherwise, <b>false</b>. </returns>
        /// <remarks>Calling this method renews pair lifespan if <see cref="IsRenewed"/> equals <b>true</b>.</remarks>
        public bool ContainsValue(TValue value)
        {
            if (!TimeoutEnabled)
                return innerDictionary.ContainsValue(value);
            else
            {
                lock (syncRoot)
                {
                    return innerDictionary.ContainsValue(value);
                }
            }
        }

        #endregion

        #region Helpers

        private void CheckStop()
        {
            if (innerDictionary.Count == 0 && ticker != null)
            {                
                ticker.Dispose();
                ticker = null;
                Debug.WriteLine("The ticker of dictionary ({0}) has been stopped."
                    , instanceId);
            }
        }

        private void CheckStart()
        {
            //consider polling
            if (TimeoutEnabled && ticker == null)
            {
                ticker = new Timer(Poll, null, _period, _period);
                Debug.WriteLine("The ticker of dictionary ({0}) has been started."
                    , instanceId);
            }
        }

        private void Poll(object state)
        {
            if (RawCount > 0)
                RemoveObsoletePairs();
        }

        private int RawCount
        {
            get
            {
                return innerDictionary.Count;
            }
        }

        private void RemoveObsoletePairs()
        {
            if (!TimeoutEnabled)
                return;

            lock (syncRoot)
            {
                DateTimeOffset timestampOut = CurrentTimeout;

                // to list because otherwise this will throw an exception during the iteration
                // modifying the collection during iteration, bad boys, bad boys, whatcha gonna do
                List<TKey> removeList =
                    (from pair in innerDictionary
                     where pair.Value.TimedOut(timestampOut)
                     select pair.Key).ToList();

                foreach (TKey key in removeList)
                    Remove(key);
            }
        }

        private DateTimeOffset CurrentTimeout
        {
            get
            {
                var timeoutSeconds = -1 * Timeout.TotalSeconds;
                return DateTimeOffset.Now.AddSeconds(timeoutSeconds);
            }
        }

        /// <summary>
        /// Gets a value indicating whether the timeout is enabled.
        /// </summary>
        /// <value><b>true</b> if timeout is enabled; otherwise, <b>false</b>.</value>
        private bool TimeoutEnabled
        {
            get { return _timeout != TimeSpan.Zero; }
        }

        /// <summary>
        /// Determines whether the entry is alive.  Renew if it still is and renew is enabled.
        /// Remove it is not.
        /// </summary>
        /// <param name="key">The key.</param>
        /// <param name="timedValue">The timed value.</param>
        /// <returns>
        /// 	<b>true</b> if the entry is alive; otherwise, <b>false</b>.
        /// </returns>
        private bool IsEntryAlive(TKey key, TimedValue<TValue> timedValue)
        {
            // it exist, so check death time
            // if it should not be dead, keep it alive
            if (timedValue.TimedOut(CurrentTimeout))
            {
                Remove(key);
                return false;
            }
            else
            {
                if (_isRenewed)
                    timedValue.Renew();

                return true;
            }
        }

        #endregion

        #region IDisposable Members

        /// <summary>
        /// Releases all resources used by the current instance of <see cref="TimedDictionary&lt;TKey, TValue&gt;"/>. 
        /// </summary>
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        private void Dispose(bool disposing)
        {
            if (!isDisposed)
            {
                if (disposing)
                {
                    if (ticker != null)
                    {
                        ticker.Dispose();
                        ticker = null;
                    }

                    Clear();
                }

                isDisposed = true;
                Debug.WriteLine("The dictionary ({0}) has been disposed.", instanceId);
            }
        }

        /// <summary>
        /// Releases unmanaged resources and performs other cleanup operations before the
        /// <see cref="TimedDictionary&lt;TKey, TValue&gt;"/> is reclaimed by garbage collection.
        /// </summary>
        ~TimedDictionary()
        {
            Dispose(false);
        }

        #endregion                

        #region IDictionary Members

        /// <summary>
        /// Adds an element with the provided key and value to the <see cref="T:System.Collections.IDictionary"/> object.
        /// </summary>
        /// <param name="key">The <see cref="T:System.Object"/> to use as the key of the element to add.</param>
        /// <param name="value">The <see cref="T:System.Object"/> to use as the value of the element to add.</param>
        /// <exception cref="T:System.ArgumentNullException">
        /// 	<paramref name="key"/> is null.
        /// </exception>
        /// <exception cref="T:System.ArgumentException">
        /// An element with the same key already exists in the <see cref="T:System.Collections.IDictionary"/> object.
        /// </exception>
        /// <exception cref="T:System.NotSupportedException">
        /// The <see cref="T:System.Collections.IDictionary"/> is read-only.
        /// -or-
        /// The <see cref="T:System.Collections.IDictionary"/> has a fixed size.
        /// </exception>
        void IDictionary.Add(object key, object value)
        {
            if (key == null)
            {
                throw new ArgumentNullException("key");
            }
            
            IfNullAndNullsAreIllegalThenThrow<TValue>(value, "value");

            try
            {
                TKey local = (TKey)key;
                try
                {
                    this.Add(local, (TValue)value);
                }
                catch (InvalidCastException)
                {
                    throw new ArgumentException(string.Format(Resources.Arg_WrongType, value, typeof(TValue)), "value");
                }
            }
            catch (InvalidCastException)
            {
                throw new ArgumentException(string.Format(Resources.Arg_WrongType,key, typeof(TKey)), "key");
            }
        }

        /// <summary>
        /// Determines whether the <see cref="T:System.Collections.IDictionary"/> object contains an element with the specified key.
        /// </summary>
        /// <param name="key">The key to locate in the <see cref="T:System.Collections.IDictionary"/> object.</param>
        /// <returns>
        /// true if the <see cref="T:System.Collections.IDictionary"/> contains an element with the key; otherwise, false.
        /// </returns>
        /// <exception cref="T:System.ArgumentNullException">
        /// 	<paramref name="key"/> is null.
        /// </exception>
        bool IDictionary.Contains(object key)
        {
            return (TimedDictionary<TKey, TValue>.IsCompatibleKey(key) && this.ContainsKey((TKey)key));
        }

        /// <summary>
        /// Returns an <see cref="T:System.Collections.IDictionaryEnumerator"/> object for the <see cref="T:System.Collections.IDictionary"/> object.
        /// </summary>
        /// <returns>
        /// An <see cref="T:System.Collections.IDictionaryEnumerator"/> object for the <see cref="T:System.Collections.IDictionary"/> object.
        /// </returns>
        IDictionaryEnumerator IDictionary.GetEnumerator()
        {            
            lock (syncRoot)
            {
                var dictionary = new Dictionary<TKey, TValue>(innerDictionary.Count);
                foreach (var pair in innerDictionary)
                {                    
                    dictionary.Add(pair.Key, pair.Value);
                }
                return dictionary.GetEnumerator();
            }
        }

        /// <summary>
        /// Gets a value indicating whether the <see cref="T:System.Collections.IDictionary"/> object has a fixed size.
        /// </summary>
        /// <value></value>
        /// <returns>true if the <see cref="T:System.Collections.IDictionary"/> object has a fixed size; otherwise, false.
        /// </returns>
        bool IDictionary.IsFixedSize
        {
            get { return false; }
        }

        /// <summary>
        /// Gets a value indicating whether this instance is read only.
        /// </summary>
        /// <value>
        /// 	<c>true</c> if this instance is read only; otherwise, <c>false</c>.
        /// </value>
        bool IDictionary.IsReadOnly
        {
            get { return true; }
        }

        /// <summary>
        /// Gets an <see cref="T:System.Collections.Generic.ICollection"/> containing the keys in the <see cref="T:System.Collections.Generic.IDictionary"/>.
        /// </summary>
        /// <returns>
        /// An <see cref="T:System.Collections.Generic.ICollection"/> containing the keys in the <see cref="T:System.Collections.Generic.IDictionary"/>.
        /// </returns>
        ICollection IDictionary.Keys
        {
            get 
            {
                lock (syncRoot)
                {
                    return innerDictionary.Keys;
                }
            }
        }

        /// <summary>
        /// Removes the element with the specified key from the <see cref="T:System.Collections.IDictionary"/> object.
        /// </summary>
        /// <param name="key">The key of the element to remove.</param>
        /// <exception cref="T:System.ArgumentNullException">
        /// 	<paramref name="key"/> is null.
        /// </exception>
        /// <exception cref="T:System.NotSupportedException">
        /// The <see cref="T:System.Collections.IDictionary"/> object is read-only.
        /// -or-
        /// The <see cref="T:System.Collections.IDictionary"/> has a fixed size.
        /// </exception>
        void IDictionary.Remove(object key)
        {
            if (TimedDictionary<TKey, TValue>.IsCompatibleKey(key))
            {
                this.Remove((TKey)key);
            }
        }

        /// <summary>
        /// Gets an <see cref="T:System.Collections.Generic.ICollection"/> containing the values in the <see cref="T:System.Collections.Generic.IDictionary"/>.
        /// </summary>
        /// <returns>
        /// An <see cref="T:System.Collections.Generic.ICollection"/> containing the values in the <see cref="T:System.Collections.Generic.IDictionary"/>.
        /// </returns>
        ICollection IDictionary.Values
        {
            get
            {
                lock (syncRoot)
                {
                    return (from value in innerDictionary.Values
                           select (TValue)value).ToArray();
                }
            }
        }

        /// <summary>
        /// Gets or sets the <see cref="System.Object"/> with the specified key.
        /// </summary>
        object IDictionary.this[object key]
        {
            get
            {
                if (TimedDictionary<TKey, TValue>.IsCompatibleKey(key))
                {
                    TValue value;
                    if (this.TryGetValue((TKey)key, out value))
                        return value;
                }
                return null;
            }
            set
            {
                if (key == null)
                {
                    throw new ArgumentNullException("key");
                }
                IfNullAndNullsAreIllegalThenThrow<TValue>(value, "value");
                try
                {
                    TKey local = (TKey)key;
                    try
                    {
                        this[local] = (TValue)value;
                    }
                    catch (InvalidCastException)
                    {
                        throw new ArgumentException(string.Format(Resources.Arg_WrongType, value, typeof(TValue)), "value");
                    }
                }
                catch (InvalidCastException)
                {
                    throw new ArgumentException(string.Format(Resources.Arg_WrongType, key, typeof(TKey)), "key");
                }
            }
        }

        private static bool IsCompatibleKey(object key)
        {
            if (key == null)
            {
                throw new ArgumentNullException("key");
            }
            return (key is TKey);
        }

        private static void IfNullAndNullsAreIllegalThenThrow<T>(object value, string argName)
        {
            if ((value == null) && (default(T) != null))
            {
                throw new ArgumentNullException(argName);
            }
        }

        #endregion

        #region ICollection Members        

        /// <summary>
        /// Copies the elements of the <see cref="T:System.Collections.ICollection"/> to an <see cref="T:System.Array"/>, starting at a particular <see cref="T:System.Array"/> index.
        /// </summary>
        /// <param name="array">The one-dimensional <see cref="T:System.Array"/> that is the destination of the elements copied from <see cref="T:System.Collections.ICollection"/>. The <see cref="T:System.Array"/> must have zero-based indexing.</param>
        /// <param name="index">The zero-based index in <paramref name="array"/> at which copying begins.</param>
        /// <exception cref="T:System.ArgumentNullException">
        /// 	<paramref name="array"/> is null.
        /// </exception>
        /// <exception cref="T:System.ArgumentOutOfRangeException">
        /// 	<paramref name="index"/> is less than zero.
        /// </exception>
        /// <exception cref="T:System.ArgumentException">
        /// 	<paramref name="array"/> is multidimensional.
        /// -or-
        /// <paramref name="index"/> is equal to or greater than the length of <paramref name="array"/>.
        /// -or-
        /// The number of elements in the source <see cref="T:System.Collections.ICollection"/> is greater than the available space from <paramref name="index"/> to the end of the destination <paramref name="array"/>.
        /// </exception>
        /// <exception cref="T:System.ArgumentException">
        /// The type of the source <see cref="T:System.Collections.ICollection"/> cannot be cast automatically to the type of the destination <paramref name="array"/>.
        /// </exception>
        void ICollection.CopyTo(Array array, int index)
        {
            if (array == null)
            {
                throw new ArgumentNullException("array");
            }

            lock (syncRoot)
            {
                var innerArray = new KeyValuePair<TKey, TimedValue<TValue>>[array.Length];
                ((ICollection)innerDictionary).CopyTo(innerArray, index);

                var objArray = array as object[];
                if (objArray == null)
                {
                    throw new ArgumentException(Resources.Argument_InvalidArrayType, "array");
                }

                for (var i = 0; i < innerArray.Length; i++)
                {
                    objArray[i] = new KeyValuePair<TKey, TValue>(innerArray[i].Key, innerArray[i].Value);
                }
            }
        }

        /// <summary>
        /// Gets a value indicating whether access to the <see cref="T:System.Collections.ICollection"/> is synchronized (thread safe).
        /// </summary>
        /// <returns>true if access to the <see cref="T:System.Collections.ICollection"/> is synchronized (thread safe); otherwise, false.
        /// </returns>
        bool ICollection.IsSynchronized
        {
            get { return true; }
        }

        /// <summary>
        /// Gets an object that can be used to synchronize access to the <see cref="T:System.Collections.ICollection"/>.
        /// </summary>
        /// <returns>
        /// An object that can be used to synchronize access to the <see cref="T:System.Collections.ICollection"/>.
        /// </returns>
        public object SyncRoot
        {
            get { return syncRoot; }
        }

        #endregion
    }
}